#include "stdafx.h"
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
#include <io.h>
#include "h2log.h"
#include "h2base.h"
#include "windows.h"

namespace H2APP
{
    map_vector<BUS>                     bus;
    map_vector<var>                     global;
    map_vector<SESSION>                 data;
};


// ============================================================================
// CONSTRUCTION STRING::unicode(const char* t)
// convert ansi string to unicode string.
// ============================================================================
STRING::unicode::unicode(const char* t) : string<wchar_t>()
{
    int len = 0;
    if(!t || (len = MultiByteToWideChar(CP_ACP, 0, t, -1, NULL, 0)) <= 0) { resize(0); return; }
    resize(len); 
    MultiByteToWideChar(CP_ACP, 0, t, -1, _buffer, _size);
}


// ============================================================================
// CONSTRUCTION STRING::unicode(ansi& t)
// convert ansi string to unicode string.
// ============================================================================
STRING::unicode&  STRING::unicode::operator =(ansi& t)
{
    int len = MultiByteToWideChar(CP_ACP, 0, *t, -1, NULL, 0);
    if(len <= 0) { resize(0); return *this; }
    resize(len);
    MultiByteToWideChar(CP_ACP, 0, *t, -1, _buffer, _size);
    return *this;
}


// ============================================================================
// CONSTRUCTION STRING::unicode(utf8& t)
// convert utf8 string to unicode string.
// ============================================================================
STRING::unicode&  STRING::unicode::operator =(utf8& t)
{
    int len = MultiByteToWideChar(CP_UTF8, 0, *t, -1, NULL, 0);
    if(len <= 0) { resize(0); return *this; }
    resize(len);
    MultiByteToWideChar(CP_UTF8, 0, *t, -1, _buffer, _size);
    return *this;
}


// ============================================================================
// CONSTRUCTION STRING::ansi(const wchar_t* t)
// convert unicode string to ansi string.
// ============================================================================
STRING::ansi::ansi(const wchar_t* t) : string<char>()
{
    int len = 0;
    if(!t || (len = WideCharToMultiByte(CP_ACP, 0, t, -1, NULL, 0, NULL, NULL)) <= 0) { resize(0); return; }
    resize(len); 
    WideCharToMultiByte(CP_ACP, 0, t, -1, _buffer, _size, NULL, NULL);
}


// ============================================================================
// CONSTRUCTION STRING::utf8(const wchar_t* t)
// convert unicode string to utf8 string.
// ============================================================================
STRING::utf8::utf8(const wchar_t* t) : string<char>()                 
{
    int len = 0;
    if(!t || (len = WideCharToMultiByte(CP_UTF8, 0, t, -1, NULL, 0, NULL, NULL)) <= 0) { resize(0); return; }
    resize(len); 
    WideCharToMultiByte(CP_UTF8, 0, t, -1, _buffer, _size, NULL, NULL);
}


// ============================================================================
// FUNCTION H2APP::send_msg()
// send BUS message
// e.g. send_msg(L"SYSTEM@quit");
// ============================================================================
bool H2APP::send_msg(const wchar_t* wmsg)
{
    STRING::unicode msg(wmsg);
    STRING::unicode name = H2APP::unpack_msg(msg);

    int idx = bus.find(*name);
    if(idx < 0) return false;

    bus[idx].push(msg);

    return true;
}


// ============================================================================
// FUNCTION H2APP::pack_msg(msg, tag)
// pack tag to a bus message
// e.g. msg = L"quit"; pack_msg(msg, L"SYSTEM") => msg = L"quit@SYSTEM"; 
// ============================================================================
void H2APP::pack_msg(STRING::unicode& msg, const wchar_t* tag)
{
    STRING::cat(msg, L"@");
    STRING::cat(msg, tag);
}


// ============================================================================
// FUNCTION H2APP::unpack_msg(msg)
// strip last tag from a bus message
// e.g. msg = L"quit@script@V8"; unpack_msg(msg) = L"V8" => msg = L"quit@script";
// ============================================================================
STRING::unicode H2APP::unpack_msg(STRING::unicode& msg)
{
    int tagpos = STRING::rfind(*msg, -1, L"@");
    if(tagpos < 0) return L"";

    STRING::unicode tag = STRING::substr(msg, tagpos+1);
    msg.resize(tagpos);
    return tag;
}

// ============================================================================
// FUNCTION H2APP::app_path()
// get current path of the exe file without the last slash '\'
// e.g. "C:\Program Files\UFEP\ufep.exe" returns "C:\Program Files\UFEP"
// ============================================================================
STRING::unicode H2APP::app_path()
{
    STRING::unicode path(MAX_PATH);
    ::GetModuleFileNameW(NULL, *path, path.size()); 

    wchar_t* pos1 = wcsrchr(*path, L'/');
    wchar_t* pos2 = wcsrchr(*path, L'\\');
    if(pos1 > pos2)
    {
        pos2 = pos1;
    }
    if(pos2)
    {
        *(pos2 + 1) = 0;
    }
    else
    {
        STRING::cat(path, L"\\");
    }

    return path;
}



// ============================================================================
// FUNCTION H2APP::app_name()
// get current exe file name without path and postfix
// e.g. "C:\Program Files\UFEP\ufep.exe" returns "fep"
// ============================================================================
STRING::unicode H2APP::app_name()
{
    STRING::ansi path(MAX_PATH);
    ::GetModuleFileNameA(NULL, *path, path.size()); 

    char* posd = strrchr(*path, '.');
    char* pos1 = strrchr(*path, '/');
    char* pos2 = strrchr(*path, '\\');
    if(posd && posd > pos1 && posd > pos2)
    {
        *posd = 0;
    }
    if(pos1 > pos2)
    {
        pos2 = pos1;
    }
    if(pos2)
    {
        memmove(*path, pos2 + 1, strlen(pos2));
    }

    path.resize(strlen(*path));

    return path;
}



// ============================================================================
// FUNCTION H2APP::time_stamp(format)
// get current date time string by specified format, e.g: "%Y-%m-%d %H:%M:%S"
// ============================================================================
STRING::unicode H2APP::time_stamp(const wchar_t* format)
{
    time_t t_now = time( 0 ); 
    tm tm_out;
    STRING::unicode str_time(64);
    localtime_s(&tm_out, &t_now);
    wcsftime(*str_time, str_time.size(), format, &tm_out); 
    return str_time; 
}



// ============================================================================
// FUNCTION H2APP::file_size(filename)
// check file size
// ============================================================================
unsigned long H2APP::file_size(STRING::unicode filename)
{
    struct _stat st;
    if(_wstat(*filename, &st) < 0)
    {
        return 0;
    }
    return (unsigned long)st.st_size;
}



// ============================================================================
// FUNCTION H2APP::load_file(filename)
// load txt file
// ============================================================================
STRING::unicode H2APP::load_file(STRING::unicode filename)
{
    int size = file_size(filename);
    if(size <= 0)
    {
        return L"";
    }

    STRING::ansi data(size + 2);
    FILE *fp;

    if(0 != _wfopen_s(&fp, *filename, L"rb"))
    {
        LOG::error(ERR_CODE_SYSTEM, __FILE__ " line %d: " __FUNCTION__ "() open file '%s' failed!", __LINE__, filename);
        return "";
    }

    int pos = 0;
    while(!feof(fp) && pos < data.size())
    {
        int len = fread(&data[pos], 1, data.size() - pos, fp);
        if(len >= 0)
        {
            pos += len;
        }
    }

    fclose(fp);

    data[size] = 0;
    data[size + 1] = 0;

    if(size > 1)
    {
        unsigned short header = *(unsigned short*)*data;

        // UTF-8
        if(header == 0xBBEF) 
        {
            return STRING::unicode(STRING::utf8((const char*)&data[3]));
        }

        // UTF-16
        if(header == 0xFFFE || header == 0xFEFF)
        {
            if(header == (htons(0xFEFF)))
            {
                char *p = *data + 2; char tmp;
                for(int i = 2; i < size; i += 2, p += 2)
                {
                    tmp = *p;
                    *p = *(p+1);
                    *(p+1) = tmp;
                }
            }
            return (wchar_t*)&data[2];
        }
    }
    return data;
}


// ============================================================================
// FUNCTION H2APP::write_file(filename, txt)
// write ansi file
// ============================================================================
bool H2APP::write_file(STRING::unicode filename, const char* txt)
{
    if(!txt)
    {
        return false;
    }

    FILE *fp;

    if(0 != _wfopen_s(&fp, *filename, L"wb"))
    {
        LOG::error(ERR_CODE_SYSTEM, __FILE__ " line %d: " __FUNCTION__ "() write file '%s' failed!", __LINE__, filename);
        return false;
    }

    int length = strlen(txt);
    int count = fwrite(txt, 1, length, fp);
    fclose(fp);
    return count == length;
}



// ============================================================================
// FUNCTION H2APP::file_exist(filename)
// check file exist or not
// ============================================================================
bool H2APP::file_exist(STRING::unicode filename)
{
    return _waccess(*filename, 0) == 0;
}
